---------------------------------------
--This module deliveres data from two FIFOS to the MCU, byte-wise on a 8-bit with bus
--The module doesn't transfer data until it receives a request from the MCU and there is content in the FIFOs to transfer.
-- It also waits for the block "sample_data_points" until it is idle and not sampling, otherwise it just ignores the request from MCU to transfer data
--
--Oussama Chammam, IC-control-project team
--KTH, MF2063
--last edit: 2012-11-15
----------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
----------------------------------------

entity data_deliverer is
	port(	
			clk : in std_logic;
			reset : in std_logic;
			
			--control signal from the block "sample_data_points"
			currently_sampling : in std_logic;
			
			--control/communicatiopn with the MCU
			data_clk : in std_logic;					--a data clock signal controlled by the MCU which is reading the FPGA output
			no_more_data_to_transfer : out std_logic;	--a control bit that tells if there is still more data in the FIFOs to be transfered to the MCU; high = no more data to transfer
			data_bus_to_MCU : out std_logic_vector(7 downto 0);	--data bus to the MCU
			
			--control lines for FIFO
			FIFO_R_clk : out std_logic;					--FIFO read clock
			FIFO_empty : in std_logic;					--high active
			sample_ADC_FIFO_RdEn: out std_logic;		--FIFO read enable signal
			sample_counter_FIFO_RdEn: out std_logic;	--FIFO read enable signal
			
			--data bus going from the FIFO
			sample_ADC_FIFO: in std_logic_vector(7 downto 0);	  
			sample_counter_FIFO: in std_logic_vector(7 downto 0) 
			
	);
end data_deliverer;  

----------------------------------------

architecture behaviour of data_deliverer is

--each value pair (counter, ADC value) consists of six bytes; therefore we transmitt it in six steps
--we even have a seventh step that is used to elaps some clock cycles to get ride of the delay effect of the FIFO
signal current_state : std_logic_vector(6 downto 0); 			--seven states (1000000, 0100000, 0010000, 0001000, 0000100, 0000010, 0000001)
signal next_state    : std_logic_vector(6 downto 0);
signal FIFO_R_clk_buffer : std_logic;							--buffer for read clock signal - for FIFO
signal data_bus_to_MCU_buffer : std_logic_vector(7 downto 0);	--buffer for data bus to the MCU
signal no_more_data_to_transfer_buffer : std_logic;
signal FIFO_delay_counter : std_logic_vector(3 downto 0);			
signal FIFO_empty_wait : std_logic;
begin

	data_bus_to_MCU <= data_bus_to_MCU_buffer;
	FIFO_R_clk <= FIFO_R_clk_buffer;
	no_more_data_to_transfer <= no_more_data_to_transfer_buffer;
	
	process(clk, reset)
	
	begin
		if reset = '1' then 						--assynchronous reset
			next_state <= "1000000";
			FIFO_delay_counter <= "1000";			--reset FIFO delay counter
			FIFO_R_clk_buffer <= '0';
			data_bus_to_MCU_buffer <= b"0000_0000";
			sample_ADC_FIFO_RdEn <= '1';
			sample_counter_FIFO_RdEn <= '1';
			no_more_data_to_transfer_buffer <= '1';
					
		elsif rising_edge(clk) then
			--if the MCU faultly sends multiple data_clk pulses before the FPGA is ready with reading the current data from the FIFO as well as preparing for next reading, the extra clock pulses will simply be ignored
			case current_state is
				when "1000000" =>							--idle state; we wait for any data clock from the MCU
					data_bus_to_MCU_buffer <= sample_ADC_FIFO;	--to finish the transmission of all data to the MCU, we need to transfer the last byte as well (only actuall if we come from state "0000010")
					sample_ADC_FIFO_RdEn <= '1';			--re-enable reading from the FIFO so it is ready for syncronizing when the MCU requestes reading again
					sample_counter_FIFO_RdEn <= '1';		--re-enable reading from the FIFO so it is ready for syncronizing when the MCU requestes reading again
					FIFO_R_clk_buffer <= '0';				--generate a falling_edge on the FIFO read clock
					FIFO_delay_counter <= "1000";			--reset delay counter so we synchronize with the FIFO when we read from them next time - when the MCU requests reading
				
					if FIFO_empty = '0' and currently_sampling = '0' then			--if the FIFO is not empty and the module "sample_data_points" is currently not sampling, then there is data to transfer
						next_state <= "0100000";			--we can move to next state
						no_more_data_to_transfer_buffer <= '0';	--tell the MCU that there is data to read
					else									--in alla other cases we should not transfer any data
						next_state <= "1000000";			--we should stay in this state
						no_more_data_to_transfer_buffer <= '1';	--tell the MCU that there is nothing more to read
					end if;
					
				when "0100000" =>							--sync with the FIFO by waiting 3 clock cycles before reading its value, and then transmitt first byte to the MCU
					if FIFO_delay_counter = "0001" and next_state = "0100000" and FIFO_R_clk_buffer = '1'then		--if this is true then we finished syncing with the FIFO
						--if next_state = "0100000" and FIFO_R_clk_buffer = '1' then	--if next_state still has the old value + the FIFO read clock is still high, that means that we still didn't generate a falling_edge on the FIFO read clock; so lets do that:
						FIFO_R_clk_buffer <= '0';		--generate a falling_edge on the FIFO read clock
						data_bus_to_MCU_buffer <= sample_counter_FIFO;	--transfer the read byte - from sample_counter_FIFO - to MCU. This is done once, and before a new read clock rising_edge is created in next cycle
					elsif FIFO_delay_counter = "0001" then								--if we already has created a falling_edge on the FIFO read clock line then:
						FIFO_R_clk_buffer <= '1';		--generate a rising_edge on the FIFO read clock to read next byte on next state
						next_state <= "0010000";		--we are now ready to transmitt next byte
						--end if;
					else									--generate 3 consecutive FIFO read clocks and count up
						if FIFO_R_clk_buffer = '0' then
							FIFO_R_clk_buffer <= '1';
							FIFO_delay_counter <= '0' & FIFO_delay_counter(3 downto 1);	--WAS: FIFO_delay_counter(0)!!!!!!! --rotate to right only when we create a rising_edge for the FIFO
						else
							if FIFO_delay_counter = "0010" then	--if we sent two clock pulses to the FIFO "sample_ADC_FIFO", then do not send more pulses so we do not get out data from it until we reach future states - where we want to read fom it
								sample_ADC_FIFO_RdEn <= '0';--set read enable signal to zero (do not read from this FIFO)
							end if;
							FIFO_R_clk_buffer <= '0';
						end if;
					end if;
					
					
				--SLOW code!! replaced above
				--when "0100000" =>							--sync with the FIFO by waiting 3 clock cycles before reading its value, and then transmitt first byte to the MCU
					--if FIFO_delay_counter = "0001" then		--if this is true then we finished syncing with the FIFO
						--if next_state = "0100000" and FIFO_R_clk_buffer = '1' then	--if next_state still has the old value + the FIFO read clock is still high, that means that we still didn't generate a falling_edge on the FIFO read clock; so lets do that:
							--FIFO_R_clk_buffer <= '0';		--generate a falling_edge on the FIFO read clock
							--data_bus_to_MCU_buffer <= sample_counter_FIFO;	--transfer the read byte - from sample_counter_FIFO - to MCU. This is done once, and before a new read clock rising_edge is created in next cycle
						--else 								--if we already has created a falling_edge on the FIFO read clock line then:
							--FIFO_R_clk_buffer <= '1';		--generate a rising_edge on the FIFO read clock to read next byte on next state
							--next_state <= "0010000";		--we are now ready to transmitt next byte
						--end if;
					--else									--generate 3 consecutive FIFO read clocks and count up
						--if FIFO_R_clk_buffer = '0' then
							--FIFO_R_clk_buffer <= '1';
							--FIFO_delay_counter <= FIFO_delay_counter(0) & FIFO_delay_counter(3 downto 1);	--rotate to right only when we create a rising_edge for the FIFO
						--else
							--if FIFO_delay_counter = "0010" then	--if we sent two clock pulses to the FIFO "sample_ADC_FIFO", then do not send more pulses so we do not get out data from it until we reach future states - where we want to read fom it
								--sample_ADC_FIFO_RdEn <= '0';--set read enable signal to zero (do not read from this FIFO)
							--end if;
							--FIFO_R_clk_buffer <= '0';
						--end if;
					--end if;
			
			
			
			
			
				when"0010000" =>							--transmitt second byte
					if next_state = "0010000" and FIFO_R_clk_buffer = '1' then	--if next_state still has the old value + the FIFO read clock is still high, that means that we still didn't generate a falling_edge on the FIFO read clock; so lets do that:
						FIFO_R_clk_buffer <= '0';			--generate a falling_edge on the FIFO read clock
						data_bus_to_MCU_buffer <= sample_counter_FIFO;	--transfer the read byte - from sample_counter_FIFO - to MCU. This is done once, and before a new read clock rising_edge is created in next cycle
					else
						FIFO_R_clk_buffer <= '1';			--create a rising_edge on the FIFO read clock
						next_state <= "0001000";			--we are now ready to transmitt next byte
					end if;
					
				when"0001000" =>							--transmitt third byte
					if next_state = "0001000" and FIFO_R_clk_buffer = '1' then	--if next_state still has the old value + the FIFO read clock is still high, that means that we still didn't generate a falling_edge on the FIFO read clock; so lets do that:
						FIFO_R_clk_buffer <= '0';			--generate a falling_edge on the FIFO read clock
						data_bus_to_MCU_buffer <= sample_counter_FIFO;	--transfer the read byte - from sample_counter_FIFO - to MCU. This is done once, and before a new read clock rising_edge is created in next cycle
					else
						FIFO_R_clk_buffer <= '1';			--create a rising_edge on the FIFO read clock
						next_state <= "0000100";			--we are now ready to transmitt next byte
					end if;
					
				when"0000100" =>							--transmitt fourth byte					
					if next_state = "0000100" and FIFO_R_clk_buffer = '1' then	--if next_state still has the old value + the FIFO read clock is still high, that means that we still didn't generate a falling_edge on the FIFO read clock; so lets do that:
						FIFO_R_clk_buffer <= '0';			--generate a falling_edge on the FIFO read clock
						sample_ADC_FIFO_RdEn <= '1';		--on next state: we want to read a byte from sample_ADC_FIFO -> enable read signal
						sample_counter_FIFO_RdEn <= '0';	--we do not want to read more bytes during next states -> disable read signal
						data_bus_to_MCU_buffer <= sample_counter_FIFO;	--transfer the read byte - from sample_counter_FIFO - to MCU. This is done once, and before a new read clock rising_edge is created in next cycle
					else
						FIFO_R_clk_buffer <= '1';			--create a rising_edge on the FIFO read clock
						next_state <= "0000010";			--we are now ready to transmitt next byte
						FIFO_empty_wait <= '0';
					end if;
					
				--when"0000010" =>							--transmitt fifth byte					
					--if FIFO_empty = '1' then				--if the FIFO become empty when we generated the read clock, then we should instead jumb to the idle state
						--next_state <= "1000000";			--we have almost finished transmission (the last byte is going to be transmitted in the idle state), go to idle state
					--elsif next_state = "0000010" and FIFO_R_clk_buffer = '1' and FIFO_empty_wait = '0' then	--if next_state still has the old value + the FIFO read clock is still high, that means that we still didn't generate a falling_edge on the FIFO read clock; so lets do that:
						--FIFO_R_clk_buffer <= '0';			--generate a falling_edge on the FIFO read clock
						--data_bus_to_MCU_buffer <= sample_ADC_FIFO;	--transfer the read byte - from sample_ADC_FIFO - to MCU. This is done once, and before a new read clock rising_edge is created in next cycle
					--elsif FIFO_R_clk_buffer = '1' then		--if we have created a rising_edge for the FIFO read clock, and it didn't become empty, then we are sure that we can move to next state (if the FIFO become empty, we will not execute this, and we will instead jumb to the idle state)
						--next_state <= "0000001";			--we are now ready to transmitt next byte
						--FIFO_R_clk_buffer <= '1';			--create a rising_edge on the FIFO read clock
					--else
						--FIFO_R_clk_buffer <= '1';			--create a rising_edge on the FIFO read clock
						--FIFO_empty_wait <= '1';					--flag to indicate that we should wait for emoty signal from the FIFO before deciding the next_state
					--end if;
				
				
				
				
				--original code that is maybe slow - modified above:
				when"0000010" =>							--transmitt fifth byte					
					if FIFO_empty = '1' then				--if the FIFO become empty when we generated the read clock, then we should instead jumb to the idle state
						next_state <= "1000000";			--we have almost finished transmission (the last byte is going to be transmitted in the idle state), go to idle state
					elsif next_state = "0000010" and FIFO_R_clk_buffer = '1' then	--if next_state still has the old value + the FIFO read clock is still high, that means that we still didn't generate a falling_edge on the FIFO read clock; so lets do that:
						FIFO_R_clk_buffer <= '0';			--generate a falling_edge on the FIFO read clock
						data_bus_to_MCU_buffer <= sample_ADC_FIFO;	--transfer the read byte - from sample_ADC_FIFO - to MCU. This is done once, and before a new read clock rising_edge is created in next cycle
					else
						FIFO_R_clk_buffer <= '1';			--create a rising_edge on the FIFO read clock
						if FIFO_R_clk_buffer = '1' then		--if we have created a rising_edge for the FIFO read clock, and it didn't become empty, then we are sure that we can move to next state (if the FIFO become empty, we will not execute this, and we will instead jumb to the idle state)
							next_state <= "0000001";		--we are now ready to transmitt next byte
						end if;
					end if;
				
				
				
				when"0000001" =>							--transmitt sixth byte					
					if next_state = "0000001" and FIFO_R_clk_buffer = '1' then	--if next_state still has the old value + the FIFO read clock is still high, that means that we still didn't generate a falling_edge on the FIFO read clock; so lets do that:
						data_bus_to_MCU_buffer <= sample_ADC_FIFO;	--transfer the read byte - from sample_ADC_FIFO - to MCU. This is done once, and before a new read clock rising_edge is created in next cycle
						FIFO_R_clk_buffer <= '0';			--generate a falling_edge on the FIFO read clock
						sample_ADC_FIFO_RdEn <= '0';		--we do not want to read more bytes during next states -> disable read signal
						sample_counter_FIFO_RdEn <= '1';	--on next state: we want to read a byte from sample_counter_FIFO -> enable read signal
					else
						FIFO_R_clk_buffer <= '1';			--create a rising_edge on the FIFO read clock
						next_state <= "0100000";			--we are now ready to transmitt next byte (start over with next 6 bytes)
					end if;

				when others	=>								--we should not come here!
					sample_ADC_FIFO_RdEn <= '1';			--re-enable reading from the FIFO so it is ready for syncronizing when the MCU requestes reading again
					sample_counter_FIFO_RdEn <= '1';		--re-enable reading from the FIFO so it is ready for syncronizing when the MCU requestes reading again
					next_state <= "1000000";				--jump to idle state
					FIFO_R_clk_buffer <= '0';				--make sure the clock is low
					FIFO_delay_counter <= "1000";			--reset delay counter so we synchronize with the FIFO when we read from them next time - when the MCU requests reading
					no_more_data_to_transfer_buffer <= '1';	--tell the MCU that there is nothing more to read
						
			end case;
		end if;
	end process;
	
	--a separate process to create the transition in the state machine
	--it is sensitive to the data_clk signal from the MCU
	process (data_clk, reset)										
	begin
	    if reset = '1' then
			current_state <= "1000000";  
		elsif falling_edge(data_clk) then						--if the MCU requests next byte of data, then:
			current_state <= next_state;					--move to next state
		end if;
	end process;
	
	
	
end behaviour;
